package com.unitd.filesystem.provider.impl;

import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.unitd.filesystem.comm.FileType;
import com.unitd.filesystem.provider.AbstractProvider;
import com.unitd.filesystem.comm.qiniu.UploadResult;
import com.unitd.filesystem.utils.HttpFilePathHelper;
import com.unitd.frame.comm.exception.FileSystemException;
import com.unitd.frame.comm.utils.FileUtils;
import org.apache.commons.io.IOUtils;
import com.unitd.frame.comm.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.UUID;

/**
 * @desc 与七牛文件服务进行上传、下载处理的接口实现类
 * @filename QiniuProvider.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/3/9
 */
public class QiniuProvider extends AbstractProvider {

	private static Logger logger = LoggerFactory.getLogger(FdfsProvider.class.getName());
	public static final String NAME = "qiniu";

	/** 七牛分布式文件系统上传管理器 */
	private static UploadManager uploadManager;
	/** 七牛分布式文件系统组管理器 */
	private static BucketManager bucketManager;
	/** 权限管理组件 */
	private Auth auth;

	/**
	 * @desc 构造方法,初始化与七牛文件服务的链接
	 * @param urlprefix 访问文件的url前缀
	 * @param bucketName 文件组
	 * @param accessKey 访问密钥
	 * @param secretKey 安全私钥
	 */
	public QiniuProvider(String urlprefix, String bucketName, String accessKey, String secretKey) {
		this.urlprefix = urlprefix.endsWith(DIR_SPLITER) ? urlprefix : urlprefix + DIR_SPLITER;
		this.bucketName = bucketName;
		auth = Auth.create(accessKey, secretKey);

		Zone z = Zone.autoZone();
		Configuration c = new Configuration(z);
		uploadManager = new UploadManager(c);
		bucketManager = new BucketManager(auth, c);
	}

	/**
	 * @desc 文件方式上传
	 * @param catalog  图片分组
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param file 需要上传的文件
	 * @return 上传后的文件URL
	 */
	@Override
	public String upload(String catalog, String fileName, File file) {
		try {
			if (fileName == null) fileName = file.getName();
			fileName = rawFileName(catalog, fileName, null);
			Response res = uploadManager.put(file, fileName, getUpToken());
			return processUploadResponse(res);
		} catch (QiniuException e) {
			processUploadException(fileName, e);
		}
		return null;
	}

	/**
	 * @desc 字节流方式上传
	 * @param catalog  图片分类
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param in 文件输入流
	 * @param fileType 文件类型信息
	 * @return 上传后的文件URL
	 */
	@Override
	public String upload(String catalog, String fileName, InputStream in, FileType fileType) {
		try {
			byte[] bs = IOUtils.toByteArray(in);
			return upload(catalog, fileName, bs, fileType);
		} catch (IOException e) {
			throw new FileSystemException(name(), e);
		}
	}

	/**
	 * @desc 字节方式上传
	 * @param catalog  图片分组
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param data 文件字节
	 * @param fileType 文件类型信息
	 * @return 上传后的文件URL
	 */
	@Override
	public String upload(String catalog, String fileName, byte[] data, FileType fileType) {
		try {
			if (fileType == null)
				fileType = FileType.getFileSuffix(data);

			fileName = rawFileName(catalog, fileName, fileType);
			Response res = uploadManager.put(data, fileName, getUpToken());
			return processUploadResponse(res);
		} catch (QiniuException e) {
			processUploadException(fileName, e);
		}
		return null;
	}

	/**
	 * @desc 从网络地址下载图片并上传
	 * @param catalog  图片分类
	 * @param fileName fileName 文件名(需确保唯一性),为空则自动生成
	 * @param origUrl 原始网络地址
	 * @return 上传后的文件URL
	 */
	@Override
	public String upload(String catalog, String fileName, String origUrl) {
		try {
			if (StringUtils.isBlank(fileName)) {
				FileType fileType = HttpFilePathHelper.parseFileType(origUrl);
				fileName = rawFileName(catalog, fileName, fileType);
			} else {
				fileName = rawFileName(catalog, fileName, null);
			}

			fileName = bucketManager.fetch(origUrl, bucketName, fileName).key;

			return getFullPath(fileName);
		} catch (QiniuException e) {
			processUploadException(fileName, e);
		}
		return null;
	}

	/**
	 * @desc 根据文件名, 删除文件系统中的文件
	 * @param fileName 指定的文件
	 * @return 0为成功，非0为失败，具体为错误代码
	 */
	@Override
	public boolean delete(String fileName) {
		try {
			if (fileName.contains(DIR_SPLITER))
				fileName = fileName.replace(urlprefix, "");
			bucketManager.delete(bucketName, fileName);
			return true;
		} catch (QiniuException e) {
			processUploadException(fileName, e);
		}
		return false;
	}

	/**
	 * @desc 下载文件, 将分布式文件系统中的指定ID的文件下载到本地指定路径
	 * @param fileId 分布式文件系统中的文件全路径
	 * @param destFile 下载的文件存放在本地的路径
	 */
	@Override
	public void download(String fileId, File destFile) {
		try {
			String downloadRUL = auth.privateDownloadUrl(fileId);
			URL remotURL = new URL(downloadRUL);
			FileUtils.copyURLToFile(remotURL, destFile);
		} catch (Exception e) {
			logger.error(e.getMessage());
			throw new FileSystemException(name(), e);
		}
	}

	/**
	 * @desc 创建上传文件锁, 用于防止上传过程中文件发生改变(此方法暂时不需事先)
	 * @param fileNames 指定的文件系统中文件
	 * @return token路径
	 */
	@Override
	public String createUploadToken(String... fileNames) {
		if (fileNames != null && fileNames.length > 0 && fileNames[0] != null) {
			return auth.uploadToken(bucketName, fileNames[0]);
		}
		return auth.uploadToken(bucketName);
	}

	/**
	 * @desc 定义的抽象方法, 用于获取使用的第三方的分布式文件系统的名称(比如: fastDFS、qiniu等)
	 * @return 分布式文件服务名称
	 */
	@Override
	public String name() {
		return NAME;
	}

	@Override
	public void close() throws IOException {
	}

	/**
	 * @desc 处理上传结果，返回文件url
	 * @param res 上传响应结果
	 * @return String
	 */
	private String processUploadResponse(Response res) throws QiniuException {
		if (res.isOK()) {
			UploadResult ret = res.jsonToObject(UploadResult.class);
			return getFullPath(ret.getKey());
		}
		throw new FileSystemException(name(), res.toString());
	}

	/**
	 * @desc 根据文件分组和名称、类型信息重新拼接上传的图片url
	 * @param catalog 文件分组
	 * @param fileName 文件名
	 * @param fileType 文件类型
	 * @return String
	 */
	private static String rawFileName(String catalog, String fileName, FileType fileType) {
		if (StringUtils.isBlank(catalog))
			catalog = "other";
		if (StringUtils.isBlank(fileName)) {
			fileName = UUID.randomUUID().toString().replaceAll("\\-", "")
					+ (fileType == null ? "" : fileType.getSuffix());
		} else if (fileType != null && !fileName.contains(".")) {
			fileName = fileName + fileType.getSuffix();
		}
		return catalog + DIR_SPLITER + fileName;
	}

	/**
	 * @desc 获取上传token，简单上传，使用默认策略
	 * @return String
	 */
	private String getUpToken() {
		return auth.uploadToken(bucketName);
	}

	/**
	 * @desc 处理上传下载异常
	 * @param fileName 文件名
	 * @param e 七牛文件异常
	 */
	private void processUploadException(String fileName, QiniuException e) {
		Response r = e.response;
		String message;
		try {
			message = r.bodyString();
		} catch (Exception e2) {
			message = r.toString();
		}
		throw new FileSystemException(fileName, e.code(), message);
	}
}